<?php

/*
 * This script belongs to Aspect PHP.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This script is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-
 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 */

/**
 * Allows to dump the aspect container to PHP code to cache it
 *
 * @package    aspectPhp
 * @subpackage lib
 * @author     gboisse
 */
class aspectContainerDumper extends aspectPointcut {

    /** @var aspectContainer The aspect container to dump */
    protected $aspectContainer;

    /**
     * Creates a new dumper for the given aspect container
     *
     * @param aspectContainer $aspectContainer The aspect container instance to dump
     */
    public function __construct(aspectContainer $aspectContainer) {
        $this->aspectContainer = $aspectContainer;
    }

    /**
     * Dumps the current aspect container to PHP code
     *
     * @param  string The class name of the generated aspect container
     * @return string The generated PHP code
     */
    public function dump($className) {

        return
        $this->startClass($className, get_class($this->aspectContainer)) .
        $this->dumpPointcuts('aroundPointcuts') .
        $this->dumpPointcuts('beforePointcuts') .
        $this->dumpPointcuts('afterReturningPointcuts') .
        $this->dumpPointcuts('afterThrowingPointcuts') .
        $this->endClass();
    }

    /**
     * Dumps the given pointcuts to PHP code
     *
     * @param  string The pointcuts name
     * @return string
     */
    protected function dumpPointcuts($pointcutsName) {
        $accessor = 'get' . aspectUtils::camelize($pointcutsName);
        $pointcuts = $this->aspectContainer->$accessor();

        $result = <<<EOF

            \$this->$pointcutsName = array(
EOF;

        $first = true;
        foreach ($pointcuts as $pointcut) {
            if ($first) {
                $first = false;
            } else {
                $result .= ',';
            }
            if ($pointcut instanceof aspectPointcut) {
                $negate = ($pointcut->negate ? 'true' : 'false');
                $arguments = $this->dumpArguments($pointcut->arguments);
                $result .= <<<EOF

                new aspectPointcut('$pointcut->aspectClassName', '$pointcut->adviceMethodName', $negate, '$pointcut->pointcutPattern', $arguments)
EOF;
            } else {
                $pointcutClass = get_class($pointcut);
                $negate = ($pointcut->negate ? 'true' : 'false');
                $result .= <<<EOF

                new $pointcutClass('$pointcut->aspectClassName', '$pointcut->adviceMethodName', $negate, '$pointcut->pointcutPattern')
EOF;
            }
        }

        return $result .
        <<<EOF

            );

EOF;
    }

    /**
     * Dumps the given arguments to PHP code
     *
     * @param  array $arguments The arguments to dump
     * @return string
     */
    protected function dumpArguments($arguments) {
        $result = 'array(';

        $first = true;
        foreach ($arguments as $key => $argument) {
            if ($first) {
                $first = false;
            } else {
                $result .= ',';
            }
            $result .= <<<EOF

                    '$key' => '$argument'
EOF;
        }

        return $result . ')';
    }

    /**
     * Starts the dumping of the PHP class
     *
     * @param string $className The name for the dumped class
     * @param string $baseClassName The name of the class which is extended
     * @return string
     */
    protected function startClass($className, $baseClassName) {
        return <<<EOF
<?php

class $className extends $baseClassName {

    public function __construct() {

        if (isset(self::\$instance)) {
            throw new LogicException('Cannot instantiate two aspect containers!');
        }

        self::\$instance = \$this;
    }

    public static function getInstance() {

        if (isset(self::\$instance)) {

            if(!isset(\$this->aroundPointcuts)) {

EOF;
    }

    /**
     * Ends the dumping of the aspect container
     *
     * @return string
     */
    protected function endClass() {
        return <<<EOF

            }

            return self::\$instance;
        }

        throw new RuntimeException('No aspect container instanciated!');
    }

}

EOF;
    }

}
